#pragma once

#include <iostream>
#include <queue>
#include <pthread.h>
#include "Log.hpp"
#include "Task.hpp"

/*
 * 线程池的线程数量
 * */
#define NUM 6

/*
 * 线程池：
 *    1.采用单例模式， 全局只有只一个线程池
 *    2.线程池的线程安全队列
 *        2.1 主线程调用accept成功之后， 创建Task对象， 将Task对象push到队列当中。Task对象当中包含新连接的套接字描述符
 *        2.2 线程池的线程从队列当中获取Task对象， 调用Task对象当中的回调函数， 进行后续业务的处理（本质上还是该线程在处
 *            理）
 *    3.采用互斥锁+条件变量的方式进行互斥+同步的处理（封装成为一个一个的接口）
 * */

class ThreadPool{
    private:
        //线程池线程的数量
        int num;
        //是否停止线程池的工作
        bool stop;
        //线程池当中的队列
        std::queue<Task> task_queue;
        //互斥锁进行保护（加锁/解锁都是由该锁保证）
        pthread_mutex_t lock;
        //条件变量保证同步
        pthread_cond_t cond;

        //由于是单例， 所以将构造函数设置成为私有的成员函数
        ThreadPool(int _num = NUM):num(_num),stop(false){
            pthread_mutex_init(&lock, nullptr);
            pthread_cond_init(&cond, nullptr);
        }

        ThreadPool(const ThreadPool &){}

        //静态实例，类外初始化
        static ThreadPool *single_instance;
    public:
        static ThreadPool* getinstance(){
            /*
             * 1.该互斥锁是保证多个线程获取实例时， 保证互斥的， 和上面队列的互斥锁不是同一个
             * 2.采用静态初始化的方式， 不用动态销毁
            */
            static pthread_mutex_t _mutex = PTHREAD_MUTEX_INITIALIZER;
            //双if判断增加效率
            if(single_instance == nullptr){
                pthread_mutex_lock(&_mutex);
                if(single_instance == nullptr){
                    //实例化对象
                    single_instance = new ThreadPool();
                    single_instance->InitThreadPool();
                }
                pthread_mutex_unlock(&_mutex);
            }
            return single_instance;
        }

        bool IsStop(){
            return stop;
        }

        /*
         * 1.判断队列是否为空
         * */
        bool TaskQueueIsEmpty(){
            return task_queue.size() == 0 ? true : false;
        }


        //互斥锁加锁接口
        void Lock(){
            pthread_mutex_lock(&lock);
        }


        //互斥锁解锁接口
        void Unlock(){
            pthread_mutex_unlock(&lock);
        }

        //条件变量等待接口
        void ThreadWait(){
            pthread_cond_wait(&cond, &lock);
        }

        //条件变量唤醒接口
        void ThreadWakeup(){
            pthread_cond_signal(&cond);
        }

        //线程的入口函数
        static void *ThreadRoutine(void *args){
            //args 是 this指针
            //强转为线程池的实例化指针， 是完全没有问题， 且能访问私有的成员变量成员函数
            ThreadPool *tp = (ThreadPool*)args;

            /*
             * 1.加锁
             * 2.循环判断队列当中是否由元素
             *     如果没有，则等待， 由push接口当中唤醒， 唤醒（唤醒之后一定是拥有互斥锁的）之后在判断
             *     如果有， 则进行第3步
             * 3.pop, 获取元素
             * 4.释放互斥锁， 这一步很关键， 一定要先释放互斥锁， 在让线程处理业务
             *   原因1：临界资源是队列， 当前线程已然执行完pop动作，该队列元素一定从队列当中移除了， 所以理应释放锁，让              *          其他线程进行处理
             *   原因2：如果不释放互斥锁， 则其他执行就阻塞在加锁。 此时如果我们处理业务， 且业务时间处理较长， 那么所有             *          的线程池的线程都在等待该业务的处理， 拖慢了整个后端的处理效率
             * 5.处理业务
             *     通过Task对象当中的回调接口， 执行业务逻辑
             * */
            while(true){
                Task t;
                tp->Lock();
                while(tp->TaskQueueIsEmpty()){
                    tp->ThreadWait();
                }
                tp->PopTask(t);
                tp->Unlock();

                //通过Task对象当中的回调接口调用业务处理函数（Protocol.hpp）
                t.ProcessOn();
            }
        }

        /*
         * 1.初始化线程池的接口
         *    创建若干个线程
         */
        bool InitThreadPool()
        {
            for(int i = 0; i < num; i++){
                pthread_t tid;
                //需要注意， 是将this指针传递给线程的入口函数当中
                if( pthread_create(&tid, nullptr, ThreadRoutine, this) != 0){
                    LOG(FATAL, "create thread pool error!");
                    return false;
                }
            }
            LOG(INFO, "create thread pool success!");
            return true;
        }

        /*
         * 队列的push接口
         *    包含了加锁和解锁， 主要给主线程使用
         * */
        void PushTask(const Task &task){
            Lock();
            task_queue.push(task);
            Unlock();
            //唤醒条件变量的PCB等待队列当中的线程， 继续处理
            ThreadWakeup();
        }

        /*
         * 队列的Pop接口
         *    没有进行加锁和解锁， 在线程入口函数当中进行了
         * */
        void PopTask(Task &task) {
            task = task_queue.front();
            task_queue.pop();
        }

        /*
         * 析构函数
         *    互斥锁销毁
         *    条件变量销毁
         * */
        ~ThreadPool(){
            pthread_mutex_destroy(&lock);
            pthread_cond_destroy(&cond);
        }
};

ThreadPool* ThreadPool::single_instance = nullptr;




